/*  
 sytax semantic analizer
 pvm_parser.y
 Scanner
 
 Created by Pei Liu on 10/23/11.
 Copyright 2011 __FOR CS440 COMPILER__. All rights reserved.
*/

%{
    #include "pvm_parser.h"
    #include "error.h"
%}

%union{
	int intval;
	char* strval;
	ENTRY* idval;
}

/*declare tokens*/
%token ANDTOKEN NOTTOKEN ORTOKEN
%token IFTOKEN ELSETOKEN ENDIFTOKEN
%token WHILETOKEN ENDWHILETOKEN DOTOKEN ENDOTOKEN
%token READTOKEN READSTRTOKEN WRITETOKEN WRITESTRTOKEN LINETOKEN
%token PROCTOKEN RETVALTOKEN ENDPROCTOKEN
%token ARRAYTOKEN VARTOKEN
%token <intval> INTTOKEN
%token <strval> IDTOKEN
%token <intval> STRTOKEN
%token <intval> CHRTOKEN
%token ADDTOKEN MINUSTOKEN MULTOKEN DIVTOKEN EQUTOKEN
%token LESSTOKEN LEQUTOKEN GEQUTOKEN GRETOKEN NEQUTOKEN
%token LPTOKEN RPTOKEN LSPTOKEN RSPTOKEN COMTOKEN SCOMTOKEN

%type <idval> _variable_ _call_
%type <intval> _add_operator_ _mult_operator_ _relational_operator_

%%

_program_: _globals_list_
{
	/*get the begining palce for the string*/
	beginstr = memocounter;
	strcounter = beginstr;
}
_procedure_list_
{
	emit(_stop_, 0, 0);//end of program
}
;

_globals_list_: _globals_list_ _globals_decl_
| /*EMPTY*/
;

_globals_decl_: _global_var_decl_
| _array_decl_
;

_global_var_decl_: VARTOKEN _global_var_list_ SCOMTOKEN
;

_global_var_list_: _global_var_list_ COMTOKEN IDTOKEN
{
	if (memocounter < MAXMEMO)
    	if(hashtbl_insert(symboltbl, $3, "variable", 1, memocounter) == -1)
        	fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, $3);
    	else
    	{
    		emit(_mvi_, memocounter, 0);
        	++memocounter;
    	}
    else
    	fprintf(stderr, SEG_ERR, lineNbr, $3);
}					
| IDTOKEN
{
    if (memocounter < MAXMEMO)
    	if(hashtbl_insert(symboltbl, $1, "variable", 1, memocounter) == -1)
        	fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, $1);
    	else
    	{
    		emit(_mvi_, memocounter, 0);
        	++memocounter;
    	}
    else
    	fprintf(stderr, SEG_ERR, lineNbr, $1);
}
;

_array_decl_: ARRAYTOKEN _array_list_ SCOMTOKEN
;

_array_list_: _array_list_ COMTOKEN _single_array_
| _single_array_
;

_single_array_: INTTOKEN IDTOKEN
{
	if ((memocounter + $1) < MAXMEMO)
    	if(hashtbl_insert(symboltbl, $2, "array", $1, memocounter) == -1)
        	fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, $2); 
        else
    	{
    		int i = 0;
    	  	for(i = 0; i < $1; i++)
    		{
    			emit(_mvi_, memocounter + i, 0);
    		}	
        	memocounter += $1;
    	 }
	else 
		fprintf(stderr, SEG_ERR, lineNbr, $2);
}
;

_procedure_list_: _procedure_list_ _procedure_decl_
| /*EMPTY*/
;

_procedure_decl_: PROCTOKEN IDTOKEN LPTOKEN _formal_parameters_ RPTOKEN _locals_list_ 
_statement_list_ ENDPROCTOKEN
{
    if(hashtbl_insert(symboltbl, $2, "procedure", formalcounter, procounter[0]) == -1)
        fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, $2);
    else
    {
        procounter[0]++;
        formalcounter = 0;
        lvcounter = 0;
        cur_proc = 0;
    }
}
;

_formal_parameters_: _formal_list_
| /*EMPTY*/
;

_formal_list_: _formal_list_ COMTOKEN IDTOKEN
{
    formalcounter++;
    
    if(hashtbl_insert(symboltbl, changeid($3), "parameters", 1, formalcounter) == -1)
    {
        fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, changeid($3));
        --formalcounter;
    }
}

| IDTOKEN
{
    formalcounter++;
    
    if(hashtbl_insert(symboltbl, changeid($1), "parameters", 1, formalcounter) == -1)
    {
        fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, changeid($1));
        --formalcounter;
    }
}
;

_locals_list_: _locals_list_ _local_decl_
| /*EMPTY*/
;

_local_decl_: VARTOKEN _local_var_list_ SCOMTOKEN
;

_local_var_list_: _local_var_list_ COMTOKEN IDTOKEN
{
    lvcounter++;
    if(hashtbl_insert(symboltbl, changeid($3), "local variables", 1, lvcounter) == -1)
    {
       fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, changeid($3));
        --lvcounter;
    }
    
}
| IDTOKEN
{
    lvcounter++;
    if(hashtbl_insert(symboltbl, changeid($1), "local variables", 1, lvcounter) == -1)
    {
        fprintf(stderr, NAME_CONFLICT_ERR, lineNbr, changeid($1));
        --lvcounter;
    }
}
;

_statement_list_: _statement_list_ _statement_ SCOMTOKEN
| /*EMPTY*/
;

_statement_: _assignment_
| _conditional_
| _loop_
| _io_
| _call_
{
    //if ($1 != NULL)
    //--cur_proc;
}
| _return_value_
;

_assignment_: _variable_ EQUTOKEN _arithmetic_expression_
{
	emit(_popd_, 501, 0);//pop _arithmetic_expression_
	emit(_popd_, 500, 0);//pop address of _variable_
	emit(_mit_, 500, 501);//copy _arithmetic_expression_ to variable
}
;

_conditional_: IFTOKEN _boolean_expression_
{
	emit(_popd_, 500, 0);
	$<intval>2 = cur_cs;//store the current position
	emit(_beq_, 0, 500);//branch when equal palce holder
}
_statement_list_ 
{
	$<intval>3 = cur_cs;
	emit(_b_, 0, 0);//branch holder
	bpatch($<intval>2 + 1, cur_cs);//end of statements, backpatch
}
_else_clause_ ENDIFTOKEN
{
	bpatch($<intval>3 + 1, cur_cs);//place when bool be false
}
;

_else_clause_: ELSETOKEN _statement_list_
| /*EMPTY*/
;

_loop_: _while_loop_
| _do_loop_
;

_while_loop_: WHILETOKEN 
{
	$<intval>1 = cur_cs;//start before evaluate bool expression
}
_boolean_expression_
{
	emit(_popd_, 500, 0);
	$<intval>2 = cur_cs;//branch here
	emit(_beq_, 0, 500);
}
_statement_list_ ENDWHILETOKEN
{
	emit(_b_, $<intval>1, 0);
	bpatch($<intval>2 + 1, cur_cs);//end of statements
}
;

_do_loop_: DOTOKEN 
{
	$<intval>1 = cur_cs;
}
_statement_list_ ENDOTOKEN WHILETOKEN _boolean_expression_
{
	emit(_popd_, 500, 0);
	emit(_bne_, $<intval>1, 500);//back to the beginning of statements list
}
;

_io_: _read_statement_
| _write_statement_ 
| _line_statement_
;

_call_: IDTOKEN LPTOKEN _actual_parameters_ RPTOKEN
{
    $$ = NULL;
    
    ENTRY* temp = hashtbl_get(symboltbl, $1);
    
    if (temp == NULL)
    	fprintf(stderr, SYMBOL_NDEFINE_ERR, lineNbr, $1);
    else
    {
        char* type = temp -> type;
        if (strcmp(type, "procedure"))
        	fprintf(stderr, TYPE_CONFLICT_ERR, lineNbr, $1);
        else
        {
            $$ = temp;
            //cur_proc++;
            //procounter[cur_proc] = temp -> location;
        }
        
    }
}
;

_return_value_: RETVALTOKEN _arithmetic_expression_
;

_actual_parameters_: _parameter_list_ 
| /*EMPTY*/
;

_parameter_list_: _parameter_list_ COMTOKEN _arithmetic_expression_
| _arithmetic_expression_
;

_quantity_: _variable_
{
	emit(_popd_, 500, 0);//pop address of variable
	emit(_mif_, 501, 500);//copy vale of variable to 501
	emit(_pushd_, 501, 0);//push value in
}
	
| INTTOKEN
{
	emit(_pushi_, $1, 0);//push yyval.intval into stack
}
| _call_
{
    //if ($1 != NULL)
    //--cur_proc;
}
| CHRTOKEN
{
	emit(_pushi_, $1, 0);//push yyval.intval into stack
}
;

_variable_: IDTOKEN
{
    $$ = NULL;
    
    ENTRY* temp = hashtbl_get(symboltbl, changeid($1));
    
    if (temp == NULL)
    	temp = hashtbl_get(symboltbl, $1);
    
    if (temp == NULL)
    	fprintf(stderr, SYMBOL_NDEFINE_ERR, lineNbr, $1);
    else
    {
        char* type = temp -> type;
        
        if (!strcmp(type, "array") || !strcmp(type, "procedure"))
        	fprintf(stderr, TYPE_CONFLICT_ERR, lineNbr, $1);
        else
        {
            $$ = temp;
            emit(_pushi_, temp -> location, 0);//push location into the stack
        }
    }
    
}
| IDTOKEN LSPTOKEN _arithmetic_expression_ RSPTOKEN
{
    $$ = NULL;
    
    ENTRY* temp = hashtbl_get(symboltbl, $1);
    
    if (temp == NULL)
    	fprintf(stderr, SYMBOL_NDEFINE_ERR, lineNbr, $1);
    else
    {
        char* type = temp -> type;
        
        if (strcmp(type, "array"))
        	fprintf(stderr, TYPE_CONFLICT_ERR, lineNbr, $1);
        else
        {
            $$ = temp;
            emit(_mvi_, 500, temp -> location);//store start location in 500
            emit(_popd_, 501, 0);//pop index in 501
            emit(_add_, 500, 501);//add start location and index
            emit(_pushd_, 500, 0);//push address in
        }
    }
}
;

_read_statement_: READTOKEN LPTOKEN _variable_ RPTOKEN
{
	emit(_popd_, 500, 0);//pop address of _variable_
	emit(_geti_, 501, 0);//get input
	emit(_mit_, 500, 501);//copy value to variable
}
| READSTRTOKEN LPTOKEN IDTOKEN RPTOKEN
{
    ENTRY* temp = hashtbl_get(symboltbl, $3);
    
    if (temp == NULL)
    	fprintf(stderr, SYMBOL_NDEFINE_ERR, lineNbr, $3);
    else
    {
        char* type = temp -> type;
        int size = temp -> size;
        
        if (strcmp(type, "array"))
        	fprintf(stderr, TYPE_CONFLICT_ERR, lineNbr, $3);
        else
        	emit(_gets_, temp -> location, 0);//gets into id location 
    }
}
;


_write_statement_: WRITETOKEN LPTOKEN _arithmetic_expression_ RPTOKEN
{
	emit(_popd_, 500, 0);//get _arithmetic_expression_
	emit(_puti_, 500, 0);//put value 
}
| WRITESTRTOKEN LPTOKEN STRTOKEN RPTOKEN
{	
	/*check if memo is enpough*/
	if ((strcounter + $3 + 1) > MAXMEMO)
		fprintf(stderr, SEG_ERR, lineNbr, "Literal String");
	else
	{
		emit(_puts_, strcounter, 0);
		/*next string beging*/
		strcounter += $3 + 1; 
	}
	
}
	
| WRITESTRTOKEN LPTOKEN IDTOKEN RPTOKEN
{
    ENTRY* temp = hashtbl_get(symboltbl, $3);
    
    if (temp == NULL)
   		fprintf(stderr, SYMBOL_NDEFINE_ERR, lineNbr, $3);
    else
    {
        char* type = temp -> type;
        
        if (strcmp(type, "array"))
        	fprintf(stderr, TYPE_CONFLICT_ERR, lineNbr, $3);
        else
        	{
        		emit(_puts_, temp -> location, 0);//write out string in location
        	}	
    }
}
;

_line_statement_: LINETOKEN
{
	emit(_line_, 0, 0);//new line
}
;

_arithmetic_expression_: _arithmetic_expression_ _add_operator_ _arithmetic_term_
{
	emit(_popd_, 501, 0);//pop _arithmetic_term_
	emit(_popd_, 500, 0);//pop _arithmetic_expression_
	emit($2, 500, 501);//add
	emit(_pushd_, 500, 0);//push into stack
}
	
| _arithmetic_term_
;

_arithmetic_term_: _arithmetic_term_ _mult_operator_ _arithmetic_factor_
{
	emit(_popd_, 501, 0);//pop _arithmetic_factor_
	emit(_popd_, 500, 0);//pop _arithmetic_term_
	emit($2, 500, 501);//mul
	emit(_pushd_, 500, 0);//push into stack
}
| _arithmetic_factor_
;

_arithmetic_factor_: _quantity_
| LPTOKEN _arithmetic_expression_ RPTOKEN
;

_add_operator_: ADDTOKEN
{
	$$ = _add_;
}
| MINUSTOKEN
{
	$$ = _sub_;
}
;

_mult_operator_: MULTOKEN
{
	$$ = _mul_;
}
| DIVTOKEN
{
	$$ = _div_;
}
;

_boolean_expression_: _boolean_expression_ ORTOKEN _boolean_term_
{
	emit(_popd_, 501, 0);
	emit(_popd_, 500, 0);
	emit(_or_, 500, 501);
	emit(_pushd_, 500, 0);
}
| _boolean_term_
;

_boolean_term_: _boolean_term_ ANDTOKEN _boolean_factor_
{
	emit(_popd_, 501, 0);
	emit(_popd_, 500, 0);
	emit(_and_, 500, 501);
	emit(_pushd_, 500, 0);
}
| _boolean_factor_
;

_boolean_factor_: NOTTOKEN _boolean_atom_
{
	emit(_popd_, 500, 0);
	emit(_not_, 500, 0);
	emit(_pushd_, 500, 0);
}
| _boolean_atom_
;

_boolean_atom_: LPTOKEN _arithmetic_expression_ _relational_operator_
_arithmetic_expression_ RPTOKEN
{
	emit(_popd_, 501, 0);
	emit(_popd_, 500, 0);
	emit(_sub_, 500, 501);
	emit($3, (cur_cs + 9), 500);
	emit(_pushi_, 0, 0);
	emit(_b_, (cur_cs + 6),0);
	emit(_pushi_, 1, 0);
}
| LPTOKEN _boolean_expression_ RPTOKEN
;

_relational_operator_: EQUTOKEN
{
	$$ = _beq_;
}
| LESSTOKEN
{
	$$ = _blt_;
}
| LEQUTOKEN
{
	$$ = _ble_;
}
| GRETOKEN
{
	$$ = _bgt_;
}
| GEQUTOKEN
{
	$$ = _bge_;
}
| NEQUTOKEN
{
	$$ = _bne_;
}
;
%%
int main(int argc, const char * argv[])
{   
	//change input to file
	if (!(yyin = fopen(argv[1], "r"))) 
    {
        fprintf(stderr, FILE_CANT_OPEN_ERR, argv[1]);
        exit(0);
    }
    
    //output
	FILE* fout = fopen("out", "w");
    
	//create symbol table
    if(!(symboltbl = hashtbl_create(32, NULL))) 
    {
		fprintf(stderr, SYMBOLTBL_FAIL_ERR);
		exit(0);
	}
	
	strlist = malloc(1000 * sizeof(char));
	
    //create code store
    cs = create_d_array();
    
    //call bison parser
	yyparse();
	
	//place header
	addheader(fout, get_used(cs), beginstr, strcounter - beginstr,0);
	
	//output the code store
	arraytrav(fout, cs);
	
	//output the string list
	strlisttrav(fout, strlist, strNbr);
	
	//clean up
	fclose(yyin);
	fclose(fout);
	hashtbl_destroy(symboltbl);
	return 0;
}

/*yyerror*/
int yyerror()
{
	fprintf(stderr, PARSE_ERR, lineNbr);
	return 1;
}
